Merge pull request #11515 from blckmn/release_artifacts
[betaflight.git] / docs / development / Atomic Barrier.md
blob353e847398128f946e660b6156f6bae00e131e1a
1 # Atomic Barrier implementation
3 ```
4 static int markme_bar = 0;
5 static int markme = 0;
7 markme++;
8 // (1) markme is read into register, but not changed
9 markme_bar++;
10 // markme_bar is read from memory and incremented
11 ATOMIC_BLOCK_NB(NVIC_PRIO_TIMER) {
12    ATOMIC_BARRIER(markme_bar);
13 // start of ATOMIC_BLOCK_NB scope:
14 //  markme_bar is stored into memory (it is input/output - "+m" output operand - of asm volatile)
15 //  BASEPRI is saved into temporary variable
16 //  BASEPRI_MAX is decremented to NVIC_PRIO_TIMER (if it is higher than NVIC_PRIO_TIMER or zero; lower number means higher priority on ARM)
17    markme++;
18 // nothing happens, markme value is not needed yet
19    markme_bar++;
20 // (2) markme_bar re-read from memory (ATOMIC_BARRIER marked it as modified - "+m" output operand of asm volatile)
21 //  and incremented
23 // end of ATOMIC_BLOCK_NB scope:
24 //  markme_bar is stored into memory (cleanup function from ATOMIC_BARRIER) / input "m" operand), but kept for later use in register
25 //    (actually markme_bar+1 is stored and pre-increment value kept in register)
26 // BASEPRI value is restored
29 markme++;
30 // register value read in (1) is incremented by 3
31 markme_bar++;
32 // register value read in (2) is incremented (actually +=2, because register contains pre-increment value)
34 // markme and markme_bar are stored into memory
35 ```
37 # Atomic Barrier Warning
40 The ATOMIC_BLOCK/ATOMIC_BARRIER construction is dependent on gcc extensions. I relies on gcc cleanup function (`attribute ((cleanup))`) and assumes that cleanup handler is called, when leaving block, even when associated variable is eliminated.
42 There is (a bit paranoid) safeguard warning to make sure that generated assembly is hand-checked on new gcc version. It is assumed that only major gcc version versions need to be checked.
44 If GCC is upgraded and a warning appears when compiling then the generated asm source must be verified.
46 e.g.
47 ```
48 %% serial_softserial.c
49 warning "Please verify that ATOMIC_BARRIER works as intended"
50 ```
52 To perform the verification, proceed as per discusson on issue #167 which reads:
54 I hope it's enough to check that optimized-away variable still has cleanup code at end of scope.
56 ```
57 static int markme=0;
58 markme++;
59 ATOMIC_BLOCK_NB(0xff) {
60    ATOMIC_BARRIER(markme);
61    markme++;
63 markme++;
64 ```
66 pass `-save-temps=obj` (or `-save-temps=cwd`, but lots of files will end up in same directory as makefile) to gcc link step (LTO is in use), find resulting `*.ltrans*.ltrans.s` (grep for `markme`, on linux it ends up in `/tmp`) and check that generated assembly sequence is:
68 ```
69                  MSR basepri_max, r3
70 # (possibly markme address load)
71                 # barier (markme) start
73 # (increment markme, load and store to memory)
74         ldr     r2, [r3]
75         adds    r0, r2, #1
76         str     r0, [r3]
78                 # barier(markme)  end
79                 MSR basepri, r3
81 # (markme value should be cached in register on next increment)
82 ```
84 The # barrier(markme) must surround access code and must be inside MSR basepri instructions .
86 Similar approach is used for ATOMIC_BLOCK in avr libraries, so gcc should not break this behavior.
88 IMO attribute(cleanup) and asm volatile is defined in a way that should guarantee this.
90 attribute(cleanup) is probably safer way to implement atomic sections - another possibility is to explicitly place barriers in code, but that can (and will eventually) lead to missed barrier/basepri restore on same path creating very hard to find bug.
92 The 'MEMORY_BARRIER()' code can be omitted when 'ATOMIC_BLOCK' (with full memory barriers) is used, but it is better to explicitly state what memory is protected by barrier. gcc 5 can use this knowledge to greatly improve generated code.